package cc.wanforme.st.server.authen.service;

import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import cc.wanforme.st.server.authen.filter.MRememberMeFilter;
import cc.wanforme.st.server.base.constant.UserType;
import cc.wanforme.st.server.base.entity.AuthToken;
import cc.wanforme.st.server.base.service.IAuthTokenService;
import cc.wanforme.st.server.properties.TokenProperty;
import cc.wanforme.st.server.util.JwtUtil;
import cc.wanforme.st.server.util.SimIpTool;


/** token 信息
 * @author wanne
 * 2020年10月23日
 */
@Service
public class TokenService {
	private static final Logger log = LoggerFactory.getLogger(TokenService.class);
	private static final int DAY_SECONDS = 24 *3600;
	
	@Autowired
	private IAuthTokenService authTokenService;
	@Autowired
	private TokenProperty tokenProperties;
	@Autowired
	private RedisTemplate<String, Object> redisTemplate;
	
	public String getToken(HttpServletRequest request) {
		return MRememberMeFilter.getToken(request, this);
	}
	
	/** 获取token
	 * @param request
	 * @return
	 */
	public String getTokenFromCookie(HttpServletRequest request) {
		String token = null;
		Cookie cookie = getCookie(tokenProperties.getName(), request);
		if( cookie != null && !"".equals(cookie.getValue())) {
			token = cookie.getValue();
		}
		return token;
	}
	
	/** 获取 Cookie
	 * @param name
	 * @param request
	 * @return
	 */
	public static Cookie getCookie(String name, HttpServletRequest request) {
		Cookie[] cookies = request.getCookies();
		Cookie re = null;
		if(cookies != null) {
			for (Cookie cookie : cookies) {
				if(cookie.getName().equals(name)) {
					re = cookie;
					// 不知道为什么同一个 cookie 会多出一个来，并且值还是空字符串
					if(re.getValue()!=null && !"".equals(re.getValue().trim())) {
						return re;
					}
				}
			}
		}
		
		return re;
	}
	
	/** 根据token查询对应的信息
	 * @param token
	 * @return
	 */
	public AuthToken selectValidToken(String token) {
		AuthToken authToken = authTokenService.selecToken(token);
		// 检查过期
		if(authToken!=null) {
			if(authToken.getExpireTime() == null || authToken.getCreateTime() == null) {
				return null;
			}
			if(LocalDateTime.now().isAfter(authToken.getExpireTime())) {
				return null;
			}
		}
		
		return authToken;
	}
	
	/** 更新 token 信息
	 * @param token
	 * @return
	 */
	public void updateToken(AuthToken authToken) {
		authTokenService.updateById(authToken);
	}
	
	/** 生成和保存 token，并返回token对象
	 * @param userId
	 * @param request
	 * @return
	 */
	public AuthToken generateAndSaveToken(long userId, String username, UserType type,
			HttpServletRequest request, HttpServletResponse response) {
//		String token = UUID.randomUUID().toString();
		String token = JwtUtil.createToken(type, userId+"", username, DAY_SECONDS);
		
		// 记录生成
		String host = SimIpTool.getRealIp(request);
		AuthToken authToken = authTokenService.saveRecord(userId, token, 
				host, tokenProperties.getExpireDays(), type.name());

		// 加入缓存
		try {
			this.cacheToken(userId, token);
		} catch (Exception e) {
			log.warn("Token can't be cached ", e);
		}
		
		// 设置 cookie
		Cookie cookie = new Cookie(tokenProperties.getName(), token);
		cookie.setMaxAge(tokenProperties.getExpireDays() * DAY_SECONDS);
		cookie.setPath("/");
		response.setHeader("Access-Control-Allow-Credentials", "true");
		response.addCookie(cookie);
		
		return authToken;
	}
	
	/** 检查 token 是否还有效
	 * @param token
	 * @return
	 */
	public boolean isValid(AuthToken token) {
		LocalDateTime now = LocalDateTime.now();
		return token.getExpireTime().isAfter(now);
	}
	
	/** token加入缓存，键token，值userId
	 * @param userId
	 * @param token
	 */
	private void cacheToken(long userId, String token) {
		String key = tokenProperties.getCachePreffix()+token;
		BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps(key);
		ops.set(userId+"");
		ops.expire(tokenProperties.getExpireDays(), TimeUnit.DAYS);
	}
	
	/** 清除token缓存
	 * @param token
	 */
	public void clearCacheToken(String token) {
		String key = tokenProperties.getCachePreffix()+token;
		redisTemplate.delete(key);
	}
	
	/** 根据token查询登录的id
	 * @param token
	 */
	public Long getIdByToken(String token) {
		try {
//			String key = tokenProperties.getCachePreffix()+token;
//			BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps(key);
//			Object re = ops.get();
//			Long id = re==null ? null : Long.parseLong((String)re);			
//			if(id!=null) {
//				return id;
//			}
			return JwtUtil.getId(token);
		} catch (Exception e) {
			log.warn("can't get id from token", e);
		}
		return null;
	}
	
	/**
	 * 清除验证信息
	 */
	public void clearAuthentications(HttpServletResponse response) {
		SecurityContextHolder.getContext().setAuthentication(null);
		Cookie cookie = new Cookie(tokenProperties.getName(), null);
		cookie.setPath("/");
		response.setHeader("Access-Control-Allow-Credentials", "true");
		response.addCookie(cookie);
	}
	
}
